/**
 * @param {string} str
 */
export function encodeURI(str) {
  return (str + "")
    .replace(/%/g, "%25")
    .replace(/=/g, "%3D")
    .replace(/\?/g, "%3F")
    .replace(/\+/g, "%2B")
    .replace(/&/g, "%26");
}
/**
 * @return {string}
 */
export function encodeQuery(data) {
  var ss = [];
  for (var k in data) {
    var v = data[k];
    if (v == null) continue;
    if (typeof v === "object") v = JSON.stringify(v);
    ss.push(encodeURI(k) + "=" + encodeURI(v));
  }
  return ss.join("&");
}
/**
 * @param {string} str
 */
export function decodeQuery(str) {
  var data = {};
  if (!str) return data;
  var ss = str.split("&");
  for (var i = 0; i < ss.length; i++) {
    var s = ss[i].split("=");
    if (s.length != 2) continue;
    var k = decodeURIComponent(s[0]);
    var v = decodeURIComponent(s[1]);
    if (/^\[{/.test(v))
      try {
        v = JSON.parse(v);
      } catch (e) {}
    data[k] = v;
  }
  return data;
}

/**
 * 获取距离1970-01-05(星期1)的天数
 * @param {Date|number} date
 */
export function getDay(date) {
  getDay.t = getDay.t || new Date("1970-01-05 00:00").getTime();
  return Math.floor((+date - getDay.t) / 86400e3);
}

/**
 * 格式化时间显示
 * @param {string} format
 * @param {number|Date} t
 */
export function format(format, t) {
  t = new Date(t);
  if (!format) {
    let now = new Date();
    if (getDay(now) - getDay(t) == 1) format = "昨天 hh:mm";
    else if (t.getFullYear() != now.getFullYear()) format = "YYYY-MM-DD hh:mm";
    else if (t.getMonth() != now.getMonth() || t.getDate() != now.getDate()) format = "MM-DD hh:mm";
    else format = "hh:mm";
  }
  var Y = (t.getFullYear() + 1e4).toString().slice(1);
  return format
    .replace(/YYYY/gi, Y)
    .replace(/YY/g, Y.slice(2))
    .replace(/MM/g, (t.getMonth() + 101).toString().slice(1))
    .replace(/DD/gi, (t.getDate() + 100).toString().slice(1))
    .replace(/hh/g, (t.getHours() + 100).toString().slice(1))
    .replace(/mm/g, (t.getMinutes() + 100).toString().slice(1))
    .replace(/ss/g, (t.getSeconds() + 100).toString().slice(1));
}

/**
 * 用v初始化def, 第一层只保留def结构，之后会复制v结构
 * @template T
 * @param {T} def
 * @param {any} v
 * @param {boolean} [dynamic]
 * @returns {T}
 */
export function deepInit(def, v, dynamic) {
  if (Array.isArray(def) && !Array.isArray(v)) return def;
  if (def && typeof def != typeof v) return def;
  if (def == null || v === null) return v;
  if (Array.isArray(def)) {
    def.length = 0;
    def.push.apply(def, v);
  } else if (typeof def === "object") {
    for (let k in def) {
      def[k] = deepInit(def[k], v[k], true);
      delete v[k];
    }
    if (dynamic)
      for (let k in v) {
        def[k] = v[k];
      }
    return def;
  }
  return v;
}

/**
 * /foo/bar -> /foo
 * @param {string} filepath
 */
export function dirname(filepath) {
  var i = filepath.length - 1;
  while (filepath[i] == "/" || filepath[i] == "\\") i--;
  for (; i >= 0; i--) {
    if (filepath[i] == "/" || filepath[i] == "\\") return filepath.slice(0, i);
  }
  return "";
}

/**
 * C:\xunjie\maincss.js 中的 maincss.js
 * @param {string} filepath
 */
export function basename(filepath) {
  var i = filepath.length - 1;
  while (filepath[i] == "/" || filepath[i] == "\\") i--;
  var end = i + 1;
  for (; i >= 0; i--) {
    if (filepath[i] == "/" || filepath[i] == "\\") return filepath.slice(i + 1, end);
  }
  return filepath.slice(0, end);
}

/**
 * C:\xunjie\maincss.js 中的 .js
 * @param {string} filepath
 */
export function extname(filepath) {
  let idx = filepath.lastIndexOf(".");
  return idx > 0 ? filepath.slice(idx).toLowerCase() : "";
}

/**
 *
 * @param {string} filepath
 * @param {string} ext
 * @returns
 */
export function replaceExt(filepath, ext = "") {
  return filepath.slice(0, -extname(filepath).length) + ext;
}

/**
 * 调整src高宽，使高度或者宽度充满容器
 * 类似css中的background-size: contain
 * @param {Size} src
 * @param {Size} dst
 */
export function contain(src, dst) {
  try {
    var r = src.width / src.height;
    var w = r * dst.height;
    if (dst.width >= w) {
      src.width = w;
      src.height = dst.height;
    } else {
      src.width = dst.width;
      src.height = dst.width / r;
    }
  } catch (e) {}
}

/**
 * 调整src高宽，使高度和宽度覆盖容器
 * 类似css中的background-size: cover
 * @param {Size} src
 * @param {Size} dst
 */
export function cover(src, dst) {
  var r = src.width / src.height;
  var w = r * dst.height;
  if (dst.width > w) {
    src.width = dst.width;
    src.height = dst.width / r;
  } else {
    src.width = w;
    src.height = dst.height;
  }
}

/**
 * 调整src高宽, 如果超过容器就进行缩放,否则保持原样
 * @param {Size} src
 * @param {Size} dst
 */
export function limitSize(src, dst) {
  let ratio = 1;
  if (src.width > dst.width) {
    ratio = dst.width / src.width;
    src.width = dst.width;
    src.height = src.height * ratio;
  }
  if (src.height > dst.height) {
    ratio = dst.height / src.height;
    src.height = dst.height;
    src.width = src.width * ratio;
  }
}
export function sleep(ms) {
  return new Promise(function(resolve) {
    setTimeout(resolve, ms);
  });
}

/**
 * 限制字符串长度, 如果超出，则显示...
 * @param {string} s
 * @param {number} n
 * @param {number} [m=0] 末尾要保留的字符数
 * @returns
 */
export function limit(s, n, m = 0) {
  let tail = s && m ? s.slice(-m) : "";
  return s ? (s.length <= n + m ? s : s.slice(0, n - 3) + "..." + tail) : "";
}

/** ** 默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1 ****/
const CHARS = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678";
const NUMBERS = "0123456789";

/**
 * @param {number} len
 */
export function randomString(len) {
  var code = "";
  for (var i = 0; i < len; i++) {
    code += CHARS.charAt(Math.floor(Math.random() * CHARS.length));
  }
  return code;
}

/**
 * @param {number} len
 */
export function randomNumber(len) {
  var code = "";
  for (var i = 0; i < len; i++) {
    code += NUMBERS.charAt(Math.floor(Math.random() * NUMBERS.length));
  }
  return code;
}

/**
 * @param {number} max
 */
export function randN(max) {
  return Math.floor(Math.random() * max);
}

/**
 * @template T
 * @param {T|T[]} val
 * @returns {T[]}
 */
export function arr(val) {
  if (val == null) return [];
  return val.length ? Array.from(val) : [val];
}

/**
 *
 * @param {any[]} list
 * @param {any[]} data
 * @param {string} key
 * @param {string} [foreignKey]
 */
export function leftJoin(list, data, key, foreignKey) {
  foreignKey = foreignKey || key + "_id";
  let map = {};
  for (let item of data) {
    map[item.id] = item;
  }
  for (let item of list) {
    let id = item[foreignKey];
    item[key] = map[id] || { id };
  }
}

/**
 * 修改body, 去掉keys和undefine
 * @template T
 * @param {T} body
 * @param {string[] | { [key: string]: any }} keys
 * @returns {T}
 */
export function clearKeys(body, keys) {
  if (keys instanceof Array) {
    // 清除keys中的字段
    for (let key of keys) {
      delete body[key];
    }
  } else {
    // 清除与keys值相同的字段
    for (let k in body) {
      let v = body[k];
      if (keys[k] == v || v === undefined) delete body[k];
    }
  }
  return body;
}

export function isEmpty(obj) {
  if (!obj) return true;
  let ok = true;
  // eslint-disable-next-line no-unreachable-loop
  for (let k in obj) {
    ok = false;
    break;
  }
  return ok;
}

const bs = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"];
/**
 * 字节数转换
 * @param {number} size
 * @param {number} [n=0] 保留几位小数
 * @param {number} [i=0] 从什么开始 b/kb/mb/gb
 * @returns
 */
export function traffic(size, n = 2, i = 0) {
  if (size == null) return "";
  size = parseInt(size);
  for (; i < bs.length; i++) {
    if (size < 1024) {
      return size + bs[i];
    }
    size = (size / 1024).toFixed(n);
  }
  return size + "YB";
}

/**
 * 字节数转换
 * @param {number} v
 * @param {number} [n=0] 保留几位小数 100为两位
 * @returns
 */
export function percent(v, n) {
  return Math.floor(v * 100 * n) / n;
}

/**
 * 间隔时间内的重复调用只有最后一次生效
 * @template T
 * @param {T} fn
 * @param {number} [delay=300]
 * @returns {T}
 */
export function debounce(fn, delay = 300) {
  var timer;
  return function() {
    var args = arguments;
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

/**
 *
 * @param {string} input
 * @returns {Uint8Array}
 */
export function fromUtf8(input) {
  const bytes = [];
  for (let i = 0, len = input.length; i < len; i++) {
    const value = input.charCodeAt(i);
    if (value < 0x80) {
      bytes.push(value);
    } else if (value < 0x800) {
      bytes.push((value >> 6) | 0b11000000, (value & 0b111111) | 0b10000000);
    } else if (
      i + 1 < input.length &&
      (value & 0xfc00) === 0xd800 &&
      (input.charCodeAt(i + 1) & 0xfc00) === 0xdc00
    ) {
      const surrogatePair = 0x10000 + ((value & 0b1111111111) << 10) + (input.charCodeAt(++i) & 0b1111111111);
      bytes.push(
        (surrogatePair >> 18) | 0b11110000,
        ((surrogatePair >> 12) & 0b111111) | 0b10000000,
        ((surrogatePair >> 6) & 0b111111) | 0b10000000,
        (surrogatePair & 0b111111) | 0b10000000
      );
    } else {
      bytes.push(
        (value >> 12) | 0b11100000,
        ((value >> 6) & 0b111111) | 0b10000000,
        (value & 0b111111) | 0b10000000
      );
    }
  }

  return Uint8Array.from(bytes);
}

/**
 * @param {Uint8Array} input
 * @returns {string}
 */
export function toUtf8(input) {
  let decoded = "";
  for (let i = 0, len = input.length; i < len; i++) {
    const byte = input[i];
    if (byte < 0x80) {
      decoded += String.fromCharCode(byte);
    } else if (byte >= 0b11000000 && byte < 0b11100000) {
      const nextByte = input[++i];
      decoded += String.fromCharCode(((byte & 0b11111) << 6) | (nextByte & 0b111111));
    } else if (byte >= 0b11110000 && byte < 0b101101101) {
      const surrogatePair = [byte, input[++i], input[++i], input[++i]];
      const encoded = "%" + surrogatePair.map(byteValue => byteValue.toString(16)).join("%");
      decoded += decodeURIComponent(encoded);
    } else {
      decoded += String.fromCharCode(
        ((byte & 0b1111) << 12) | ((input[++i] & 0b111111) << 6) | (input[++i] & 0b111111)
      );
    }
  }

  return decoded;
}

/**
 * 检查名称是否可用
 * @param {string} name
 * @param {Set<string>|string[]} names
 * @param {(s:string)=>string} [fn]
 */
export function checkRename(name, names, fn) {
  let nameset = Array.isArray(names) ? new Set(names) : names;
  if (!fn)
    fn = s =>
      s.replace(/(\(\d+\))?(\.\w+)?$/, (_, x, ext) => (x ? `(${parseInt(x.slice(1)) + 1})` : `(1)`) + (ext || ""));
  while (nameset.has(name)) name = fn(name);
  return name;
}

/**
 * @template T
 * @returns {Promise<T>&{resolve(x?:T)=>void,reject(e:any)=>void}}
 */
export function newPromise() {
  var tmp;
  var pms = new Promise(function(resolve, reject) {
    tmp = { resolve, reject };
  });
  return Object.assign(pms, tmp);
}

/**
 * 判断按键是否用于输入文本
 * Nice: http://unixpapa.com/js/key.html
 * @param {{keyCode:number;ctrlKey?:boolean;metaKey?:boolean;altKey?:boolean;}} e
 * @returns
 */
export function isIntendToInput(e) {
  if (e.ctrlKey || e.metaKey || e.altKey) return false;

  // a-zA-Z
  if (e.keyCode >= 65 && e.keyCode <= 90) return true;

  // 0-9 以及其上面的符号
  if (e.keyCode >= 48 && e.keyCode <= 57) return true;

  // 小键盘区域 (除回车外)
  if (e.keyCode != 108 && e.keyCode >= 96 && e.keyCode <= 111) return true;

  // 小键盘区域 (除回车外)
  // @yinheli from pull request
  if (e.keyCode != 108 && e.keyCode >= 96 && e.keyCode <= 111) return true;

  // 输入法
  if (e.keyCode == 229 || e.keyCode === 0) return true;

  return false;
}

export function isChineseMobilePhone(phone) {
  return /^([1-9]{1,3}-)?1[3-9][0-9]{9}$/.test(phone);
}

/** 判断是否邮箱格式，不支持中文邮箱 */
export function isEmail(email) {
  return /^[\w-]+@[\w-]+(\.[\w-]+)+$/.test(email);
}

export function getuuid() {
  let uuid = "";
  let s = [];
  let hexDigits = "0123456789abcdef";
  for (let i = 0; i < 36; i++) {
    s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
  }
  s[14] = "4";
  s[19] = hexDigits.substr((+s[19] & 0x3) | 0x8, 1);
  s[8] = s[13] = s[18] = s[23] = "";
  uuid = s.join("");
  return uuid;
}
export * from "./webtools";

export * from "./business";
